Skip to content

Gate the default focus refetch on lost realtime coverage - #2278

Closed
vburojevic wants to merge 1 commit into
get-bb:mainfrom
vburojevic:perf/mobile-gaps-unlock-refetch
Closed

Gate the default focus refetch on lost realtime coverage#2278
vburojevic wants to merge 1 commit into
get-bb:mainfrom
vburojevic:perf/mobile-gaps-unlock-refetch

Conversation

@vburojevic

Copy link
Copy Markdown
Contributor

What was wrong

Every window focus refetched every active query older than staleTime 2s. On phones each unlock/app-switch fired that full wave on top of the realtime reconnect wave (visible-probe + reconnect watermark + hidden-change replay), duplicating catch-up in the first interactive frames.

What changed

createAppQueryClient gains an injectable shouldRefetchOnWindowFocus; main gates it on wsManager.getConnectionState() !== "connected". While connected, realtime owns freshness; in connecting/reconnecting the focus refetch remains the fallback. Per-query policies (query-policies.ts) still win.

Known narrow trade (disclosed for review)

A half-open socket reports "connected" up to the 5s pong timeout, and queries with neither realtime coverage nor a focus policy (e.g. CLI skills status) skip one focus wave when parked across an unlock; refetchOnMount repairs them on navigation.

How verified

Gated + ungated focus tests (5/5); typecheck + oxlint clean; adversarially reviewed.

🤖 Generated with Claude Code

Every window focus refetched every active query older than 2s. On a
phone that meant each unlock and app switch fired a full refetch wave on
top of the realtime reconnect wave: the WebSocket manager already probes
the socket on visible, the reconnect watermark refetches exactly the
queries whose data predates the disconnect, and changes merged while
hidden flush on the next visible. The focus wave duplicated all of that
in the first interactive frames after unlock.

Make the default focus refetch injectable and gate it in main on
wsManager's connection state: while the state is "connected", realtime
owns freshness; in "connecting" or "reconnecting" the focus refetch
remains the fallback. Per-query refetchOnWindowFocus policies
(query-policies.ts) are unaffected and still win.

Known narrow trade, called out for review: a half-open socket reports
"connected" for up to the 5s pong timeout, and the handful of queries
with neither realtime coverage nor a focus policy (e.g. CLI skills
status) skip one focus wave when parked across an unlock; refetchOnMount
still repairs them on navigation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
SawyerHood added a commit that referenced this pull request Aug 22, 2026
…ttach edge (#2300)

Lands five mobile-performance PRs by Vedran Burojevic plus one follow-up fix.

* #2277 Keep the thread panel host mounted across thread navigation
* #2278 Gate the default focus refetch on lost realtime coverage
* #2279 Apply urgent realtime thread changes without flushing the debounce buffer
* #2280 Cache max scroll offset outside the timeline scroll hot path
* #2281 Make the published plugin composer host stable across keystrokes

Follow-up fix: a detached timeline viewport that a content shrink clamps onto
the new, smaller maximum stayed detached, because the clamp's scroll event
outruns the ResizeObserver refresh and is classified against the stale cache.
The ResizeObserver path now detects that case and re-attaches. Regression test
fails on the #2280 head and passes here.

Verified: typecheck pass; oxlint 0 errors; @bb/app 422 files / 3,288 tests pass.

Merge commits are disabled on this repository, so this squash carries the work
of all five branches; co-author trailers preserve authorship.

Co-Authored-By: Vedran Burojevic <vedran.burojevic@gmail.com>
Co-Authored-By: Claude <noreply@anthropic.com>
@SawyerHood

Copy link
Copy Markdown
Collaborator

Shipped in #2300 (squash commit 836e216).

This branch's changes are on main verbatim — I reviewed all five of your mobile-performance PRs (#2277#2281) for regressions, merged them onto one integration branch, and landed the batch together after a full verification pass (typecheck, oxlint, and the whole @bb/app suite: 422 files / 3,288 tests green).

Closing as superseded rather than merged only because merge commits are disabled on this repo: a squash creates a new commit, so GitHub cannot mark this PR merged even though its changes are in main. Your authorship is preserved as a Co-Authored-By trailer on the squash commit.

Review notes: no code regression. One disclosed trade accepted by the owner — queries with neither realtime coverage nor a focus policy (Skills, plugin settings/contributions/catalog, host-path, changelog, plugin SDK useSettings) skip the focus refresh while the socket reports connected and repair on remount.

Thanks — nice work on this batch.

AGENT GENERATED

@SawyerHood SawyerHood closed this Aug 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants